package com.songyang.yougnsql.server;

import com.songyang.yougnsql.common.ErrorMsg;
import com.songyang.yougnsql.common.LogHelper;
import com.songyang.yougnsql.common.YoungSQLException;
import com.songyang.yougnsql.core.parser.SqlParser;
import com.songyang.yougnsql.core.parser.statement.*;
import com.songyang.yougnsql.core.table.TableManager;
import com.songyang.yougnsql.core.table.res.BeginRes;
import com.songyang.yougnsql.server.serialize.inter.YoungSqlSerialize;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import java.nio.charset.StandardCharsets;
import java.util.Arrays;

/**
 * 执行sql的类
 */
public class Executor {
    private static Executor executor;
    private final TableManager tbm;
    private final Logger logger = LogManager.getLogger(Executor.class);
    private long xid;

    public Executor(TableManager tbm) {
        this.tbm = tbm;
        this.xid = 0;
    }

    public static Executor getExecutor() {
        return executor;
    }

    public static void initExecutor(TableManager tbm) {
        Executor.executor = new Executor(tbm);
    }

    public void close() {
        if (xid != 0) {
            System.out.println("Abnormal Abort: " + xid);
            tbm.abort(xid);
        }
    }

    public String execute(byte[] sqlRaw) throws Exception {
        String[] sqls = new String(Arrays.copyOfRange(sqlRaw, YoungSqlSerialize.DATA_OFFSET - 1, sqlRaw.length)).split(";");
        String nowSql = null;
        try {
            for (String s : sqls) {
                if ("".equals(s)) continue;
                byte[] sql = s.getBytes(StandardCharsets.UTF_8);
                nowSql = new String(sql);
                LogHelper.info(logger, "执行sql:{}", nowSql);
                Object stat = SqlParser.Parse(sql);
                if (stat instanceof Begin) {
                    if (xid != 0) {
                        throw new YoungSQLException(ErrorMsg.NESTED_TRANSACTION_EXCEPTION);
                    }
                    BeginRes r = tbm.begin((Begin) stat);
                    xid = r.xid;
                    return r.result;
                } else if (stat instanceof Commit) {
                    if (xid == 0) {
                        throw new YoungSQLException(ErrorMsg.NO_TRANSACTION_EXCEPTION);
                    }
                    String res = tbm.commit(xid);
                    xid = 0;
                    return res;
                } else if (stat instanceof Abort) {
                    if (xid == 0) {
                        throw new YoungSQLException(ErrorMsg.NO_TRANSACTION_EXCEPTION);
                    }
                    String res = tbm.abort(xid);
                    xid = 0;
                    return res;
                } else {
                    return execute2(stat, s);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
            return e.getMessage();
        }
        return "未知错误";
    }

    private String execute2(Object stat, String sql) throws Exception {
        boolean tmpTransaction = false;
        Exception e = null;
        if (xid == 0) {
            tmpTransaction = true;
            BeginRes r = tbm.begin(new Begin());
            xid = r.xid;
        }
        try {
            String res = null;
            if (stat instanceof Show) {
                res = tbm.show(xid);
            } else if (stat instanceof Create) {
                res = tbm.create(xid, (Create) stat);
            } else if (stat instanceof Select) {
                //如果有缓存 直接返回结果
//                if (SqlResultCache.getSqlResultCache().containsKey(sql)) {
//                    return (String) SqlResultCache.getSqlResultCache().get(sql);
//                }
//                tbm.read(xid, (Select) stat)
                res = tbm.read(xid, (Select) stat);
            } else if (stat instanceof Insert) {
                res = tbm.insert(xid, (Insert) stat);
            } else if (stat instanceof Delete) {
                res = tbm.delete(xid, (Delete) stat);
            } else if (stat instanceof Update) {
                res = tbm.update(xid, (Update) stat);
            }
            return res;
        } catch (Exception e1) {
            e = e1;
            throw e;
        } finally {
            if (tmpTransaction) {
                if (e != null) {
                    tbm.abort(xid);
                } else {
                    tbm.commit(xid);
                }
                xid = 0;
            }
        }
    }
}
